﻿using System;
using System.Net;
using System.Threading;
using System.Net.Sockets;
using Tensop.Net.Tcp.Protocol;
using System.Collections.Generic;

namespace Tensop.Net.Tcp
{
    public abstract class Connector : IDisposable
    {
        private const int                               m_ReceivePage = 4096;
        private const int                               m_MaxReceiveSize = 16777216;


        private Socket                                  m_server;
        private int                                     m_MaxConnection;
        private int                                     m_CurrentConnection;
        private Semaphore                               m_Wait;
        private IPEndPoint                              m_localEndPoint;
        private bool                                    _FirstStart;

        private Stack<Client>                           m_Pool;
        private MemoryAlloc                             m_Alloc;//静态分配
        private Dictionary<int, Client>                 m_Online;

        #region 事件

        public event ClientConnectEventHandler                      Connected;
        public event ReceiveCompletedEventHandler                   Receive;
        public event Action                                         Send;
        public event ClientConnectExitEventHandler                  Exit;
        #endregion

        #region 属性
        public int CurrentConnection { get => m_CurrentConnection; }

        public IPEndPoint LocalEndPoint { get => m_localEndPoint; }

        #endregion

        public Connector(IPEndPoint server, int maxConnection)
        {
            m_server = new Socket ( server.AddressFamily, SocketType.Stream, ProtocolType.Tcp );

            m_Wait = new Semaphore ( maxConnection, maxConnection );
            m_Pool = new Stack<Client> ( maxConnection );
            m_Online = new Dictionary<int, Client> ();
            m_Alloc = new MemoryAlloc ( m_ReceivePage, maxConnection );

            for ( int i = 0; i < maxConnection; i++ )
            {
                m_Pool.Push ( new Client ( i, m_ReceivePage, IO_Completed ) );
            }

            m_MaxConnection = maxConnection;
            m_CurrentConnection = 0;
            m_localEndPoint = server;
            m_server.Bind ( server );
            m_server.Listen ( 128 );
        }



        #region 连接处理部分
        private void StartAccept(SocketAsyncEventArgs e)
        {
            if ( e == null )
            {
                e = new SocketAsyncEventArgs ();
                e.Completed += IO_Completed;
            }
            else
            {
                e.AcceptSocket = null;
            }

            m_Wait.WaitOne ();
            if ( !m_server.AcceptAsync ( e ) )
            {
                EndAccept ( e );
            }
        }
        private void EndAccept(SocketAsyncEventArgs e)
        {
            if ( e.SocketError == SocketError.Success && e.AcceptSocket.Connected )
            {
                var s = e.AcceptSocket;
                var client = m_Pool.Pop ();
                Binding ( client, s );

                StartReceive ( client );
                Connected?.Invoke ( client.Id );
                m_Online.Add ( client.Id, client );
            }
            StartAccept ( e );
        }
        #endregion

        #region 数据接收处理部分

        private void StartReceive(Client client)
        {
            if ( !client.Base.ReceiveAsync ( client.Read ) )
            {
                try
                {
                    using ( var reader = new NetworkStream ( client.Base ) )
                    {
                        if ( client.ContentLength == -1 )
                        {
                            //Signal
                            var data = new byte[20];
                            reader.Read ( data, 0, data.Length );

                            if ( HeaderProtocol.Validate ( data ) )
                            {
                                var signal = new HeaderProtocol ( data );
                                client.ContentLength = signal.ContentLength;
                                client.EventArgs = new ServerReceiveEventArgs ( client, signal.Protocol, signal.Version );
                            }
                            else
                            {
                                Echos ( client, EchoFactory.ReplyClient ( false, false ) );
                            }

                        }
                        else
                        {
                            if ( client.ContentLength > 0 || client.ContentLength <= int.MaxValue )
                            {
                                var data = new byte[client.ContentLength];
                                reader.Read ( data, 0, data.Length );
                                IO_Completed ( new ArraySegment<byte> ( data, 0, data.Length ), client.Read );
                            }
                        }
                    }
                    StartReceive ( client );
                }
                catch
                {
                    //处理连接中断
                }
            }
        }
        private void StartReceive(SocketAsyncEventArgs e)
        {
            var client = e.UserToken as Client;
            StartReceive ( client );
        }

        //处理异步接收
        private void AsyncReceiveProc(SocketAsyncEventArgs e)
        {
            var client = e.UserToken as Client;
            if ( e.SocketError == SocketError.Success && e.BytesTransferred > 0 && client.Base.Connected)
            {
                var data = m_Alloc.Copy ( e );
                SyncReceiveProc ( data, e );
                StartReceive ( e );
            }
            else
            {
                StartCloseConnection ( e );
            }
        }

        private void SyncReceiveProc(ArraySegment<byte> data, SocketAsyncEventArgs e)//处理验证逻辑
        {
            var client = e.UserToken as Client;
            OnReceive ( data, client );
        }

        //private void DetachLowLevelProtocol(ArraySegment<byte> data, Client e)//处理数据
        //{
        //    if ( e.ContentLength == -1 )
        //    {
        //        if ( Echo.Validate ( data ) )
        //        {
        //            var echo = new Echo ( data );
        //            if ( echo.Protocol == 0 && echo.Version == 13 )//接收数据包
        //            {
        //                e.EventArgs = new ServerReceiveEventArgs ( e, echo.Protocol, echo.Version );
        //                e.ContentLength = echo.ContentLength;
        //                Echos ( e, EchoFactory.ReplyClient ( false, true ) );
        //            }
        //            else if ( echo.Protocol == 0 && echo.Version == 9 )//客户端已经接收到传输Echo
        //                EndSendProc ( e );
        //        }
        //        else
        //        {
        //            Echos ( e, EchoFactory.ReplyClient ( false, false ) );
        //        }
        //    }
        //    else
        //    {
        //        e.EventArgs.Put ( data );
        //        if ( e.ContentLength == 0 )
        //        {
        //            OnReceive ( FinishReceiveProcedure ( e ), e.id );
        //            e.ContentLength = -1;
        //        }
        //    }

        //}
        protected ServerReceiveEventArgs FinishReceiveProcedure(Client client)
        {
            var e = client.EventArgs;
            foreach ( var i in client.EventArgs.Get () )
            {
                m_Alloc.Push ( i );
            }
            return e;
        }

        protected virtual void OnReceive(ArraySegment<byte> data,Client c)
        {

        }
        protected virtual void OnReceive(ServerReceiveEventArgs args, int id)
        {
            Receive?.Invoke ( id, args );
        }

        #endregion

        #region 发送处理部分
        private void StartSendProc(Client c, byte[] data)
        {
            c.SendBuffer = data;
            var echo = EchoFactory.PrepareReceive ( data.Length );
            Echos ( c, echo );
        }
        private void EndSendProc(Client c)
        {
            try
            {
                var data = c.SendBuffer;
                if ( data.Length > m_ReceivePage )
                {
                    Array.Copy ( data, c.Write.Buffer, data.Length );
                    c.Write.SetBuffer ( 0, data.Length );
                }
                else
                {
                    Array.Copy ( data, 0, c.Write.Buffer, 0, data.Length );
                    c.Write.SetBuffer ( 0, data.Length );
                }
                Send?.Invoke ();
            }
            catch
            {
                StartCloseConnection ( c );
            }
        }
        private void EndSendProc(SocketAsyncEventArgs e)
        {
            var c = e.UserToken as Client;
            try
            {

                if ( c != null && e.SocketError == SocketError.Success )
                {
                    c.Write.SetBuffer ( 0, c.BufferSize );
                    c.SendBuffer = null;
                }
                Send?.Invoke ();
            }
            catch
            {
                StartCloseConnection ( c );
            }
        }
        #endregion

        #region 连接关闭部分
        private void StartCloseConnection(SocketAsyncEventArgs e)
        {
            StartCloseConnection ( e.UserToken as Client );
        }
        private void StartCloseConnection(Client e)
        {
            m_Wait.Release ();
            Debinding ( e );
            m_Pool.Push ( e );
            m_Online.Remove ( e.Id );
            EndCloseConnection ( e.Id );
        }
        private void EndCloseConnection(int id)
        {
            Exit?.Invoke ( id );
        }


        private void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch ( e.LastOperation )
            {
                case SocketAsyncOperation.Accept:
                    EndAccept ( e );
                    break;
                case SocketAsyncOperation.Disconnect:
                    StartCloseConnection ( e );
                    break;
                case SocketAsyncOperation.Receive:
                    if ( sender is ArraySegment<byte> data )
                        SyncReceiveProc ( data, e );//可能该行代码不会执行到，因为e.LastOperation可能不是Receive
                    else
                        AsyncReceiveProc ( e );
                    break;
                case SocketAsyncOperation.Send:
                    EndSendProc ( e );
                    break;
            }
        }
        #endregion

        #region Public

        /// <summary>
        /// 启动服务
        /// </summary>
        public void Start()
        {
            if ( !_FirstStart )
            {
                _FirstStart = true;
                StartAccept ( null );
            }
        }

        /// <summary>
        /// 向指定客户端发送数据
        /// </summary>
        /// <param name="data">待发送的数据</param>
        /// <param name="id">客户端唯一标识符</param>
        public void SendTo(byte[] data, int id)
        {
            var c = m_Online[id];
            if ( c != null && data != null )
            {
                StartSendProc ( c, data );
            }
        }

        /// <summary>
        /// 关闭服务
        /// </summary>
        public void Close()
        {
            Dispose ();
        }

        /// <summary>
        /// 向客户端发送一次Echo
        /// </summary>
        /// <param name="id"></param>
        /// <param name="data"></param>
        public void Echos(int id, byte[] data)
        {
            var c = m_Online[id];
            if ( c != null && data != null )
            {
                StartSendProc ( c, data );
            }
        }

        internal void Echos(Client c, HeaderProtocol signal)
        {
            try
            {
                var d = signal.ToArray ();
                Array.Copy ( d, c.Write.Buffer, d.Length );
                c.Write.SetBuffer ( 0, d.Length );
            }
            catch
            {
                StartCloseConnection ( c );
            }
        }

        /// <summary>
        /// 关闭指定身份的连接
        /// </summary>
        /// <param name="id"></param>
        public void Shutdown(int id)
        {
            try
            {
                var c = m_Online[id];
                StartCloseConnection ( c );
            }
            catch { }

        }

        /// <summary>
        /// 释放服务占用的资源
        /// </summary>
        public void Dispose()
        {
            try
            {
                m_server.Shutdown ( SocketShutdown.Both );
            }
            catch { }
            finally
            {
                m_server.Disconnect ( true );
                m_server.Dispose ();
            }
            m_Online.Clear ();
            m_Pool.Clear ();
            m_Wait.Dispose ();
            m_Online = null;
            m_Pool = null;
            GC.SuppressFinalize( this );
        }
        #endregion

        #region ClientLifeTime

        private void Binding(Client client, Socket s)
        {
            client.Base = s;
            client.StartTime = DateTime.Now;
        }

        private void Debinding(Client client)
        {
            client.Connected = false;
            client.ContentLength = -1;
            try
            {
                client.Base.Shutdown ( SocketShutdown.Both );
            }
            catch { }
            finally
            {
                client.Base.Disconnect ( false );
                client.Base.Dispose ();
            }
            client.Base = null;
            client.StartTime = client.EndTime = Client.m_DefaultTime;
        }

        #endregion
    }
}
